{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SOLT Calibration Standards Creation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Introduction\n",
    "\n",
    "In scikit-rf, a calibration standard is treated just as a regular one-port or\n",
    "two-port `skrf.Network`, defined by its full S-parameters. It can represent\n",
    "reflection, transmission, load, and even arbitrary-impedance standards. Since\n",
    "no additional errors are introduced in circuit modeling and fitting, this\n",
    "approach allows the highest calibration accuracy, and is known as a\n",
    "*data-based standard* in the terminology of VNA vendors.\n",
    "\n",
    "However, traditionally, VNA calibration standards are defined using a circuit\n",
    "model with fitted coefficients. Since many such calibration kits are still\n",
    "being used and manufactured, especially in low-frequency applications, this\n",
    "necessitates the creation of their network models before they can be used in\n",
    "scikit-rf's calibration routines.\n",
    "\n",
    "This example explains network creation from coefficients given in both the\n",
    "HP-Agilent-Keysight format and the Rohde & Schwarz / Anritsu format. Both\n",
    "are essentially the same circuit model, but the latter format uses different\n",
    "units of measurement for an offset transmission line.\n",
    "\n",
    ".. warning::\n",
    "Only coaxial standards are covered by this guide. The calculation is different\n",
    "for waveguides. In particular, the scaling by $\\sqrt{\\text{GHz}}$ for coaxial\n",
    "lines cannot be applied to waveguides because loss is also a function of their\n",
    "physical dimensions, with significant more complicated formulas. Do you have\n",
    "waveguide experience? If so, you can help by\n",
    "[contributing](../../contributing/index.rst#examples-and-tutorials) to the doc.\n",
    "\n",
    "\n",
    "## Alternatives to scikit-rf Modeling\n",
    "\n",
    "Before we begin, it's worth pointing out some alternatives.\n",
    "\n",
    "In scikit-rf, you are able to use any existing standard definition by\n",
    "its S-parameters. If you already have your standard defined as a network\n",
    "in other tools (e.g. in your favorite circuit simulator, or actual\n",
    "measurements results), you can simply export the S-parameters to Touchstone\n",
    "files for use in scikit-rf. Similarly, if you're already using a data-based\n",
    "calibration standard, it should be possible to use its data directly. The\n",
    "S-parameters may be stored in device-specific file formats, consult your\n",
    "vendor on whether they can be exported as a Touchstone file.\n",
    "\n",
    "Finally,\n",
    "for non-critical measurements below 1 GHz, sometimes one can assume\n",
    "the calibration standards are ideal. In scikit-rf, one can create ideal\n",
    "responses conveniently by defining an ideal transmission line and calling\n",
    "the `short()`, `open()`, `match()`, and `thru()` methods (explained\n",
    "in the [Preparation](#preparation) section).\n",
    "\n",
    "\n",
    "## Example: HP-Agilent-Keysight Coefficient Format\n",
    "\n",
    "After the necessary background is introduced, let's begin.\n",
    "\n",
    "For the purpose of this guide, we're going to model the Keysight 85033E,\n",
    "3.5 mm, 50 Ω, DC to 9 GHz calibration kit, with the following coefficients\n",
    "[[4](#ref4)].\n",
    "\n",
    "|   Parameter  |            Unit             |    Open   |   Short   |  Load   |   Thru   |\n",
    "| ------------ | --------------------------- | --------- | --------- | ------- | -------- |\n",
    "| $\\text{C}_0$ |  $10^{−15} \\text{ F}$       |   49.43   |           |         |          |\n",
    "| $\\text{C}_1$ |  $10^{−27} \\text{ F/Hz}$    | -310.1    |           |         |          |\n",
    "| $\\text{C}_2$ |  $10^{−36} \\text{ F/Hz}^2$  |   23.17   |           |         |          |\n",
    "| $\\text{C}_3$ |  $10^{−45} \\text{ F/Hz}^3$  |   -0.1597 |           |         |          |\n",
    "| $\\text{L}_0$ |  $10^{−12} \\text{ H}$       |           |    2.077  |         |          |\n",
    "| $\\text{L}_1$ |  $10^{−24} \\text{ H/Hz}$    |           | -108.5    |         |          |\n",
    "| $\\text{L}_2$ |  $10^{−33} \\text{ H/Hz}^2$  |           |    2.171  |         |          |\n",
    "| $\\text{L}_3$ |  $10^{−42} \\text{ H/Hz}^3$  |           |   -0.01   |         |          |\n",
    "|  Resistance  |         $\\Omega$            |           |           |    50   |          |\n",
    "| Offset $Z_0$ |         $\\Omega$            |    50     |     50    |    50   |    50    |\n",
    "| Offset Delay |            ps               |  29.242   |   31.785  |     0   |     0    |\n",
    "| Offset Loss  | $\\text{G}\\Omega$ / s        |   2.2     |    2.36   |     2.3 |     2.3  |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Circuit Model\n",
    "\n",
    "Before we start creating their network definitions, we first need to know\n",
    "the underlying circuit model and the meaning of these coefficients.\n",
    "\n",
    "![Circuit Schematic](calkit-schematic.svg)\n",
    "\n",
    "As this schematic shows, this is the HP-Agilent-Keysight model for\n",
    "a calibration standard.\n",
    "\n",
    "The first part is an \"offset\" lossy transmission\n",
    "line, defined using three parameters: (1) A real characteristic impedance\n",
    "of a lossless line. This is usually the system impedance, and it matches the\n",
    "VNA port impedance. However, sometimes a value slightly different from the port\n",
    "impedance is used to model imperfections in the standard. For example, 50.209\n",
    "Ω or 49.992 Ω. Also, waveguide standards use a special normalized value `1`.\n",
    "(2) A delay - represents its electrical\n",
    "length, given in picoseconds, and (3) a loss. The loss is given in a somewhat\n",
    "unusual unit - gigaohms per second. With the three parameters, one can\n",
    "calculate the *propagation constant* ($\\gamma$) and the complex\n",
    "*characteristic impedance* ($Z_c$) of the lossy line.\n",
    "\n",
    "A shunt impedance is connected at the end of the transmission line,\n",
    "and models the distributed capacitance or inductance\n",
    "in the open or short standard. It's given as a third-degree\n",
    "polynomial with four coefficients, $y(x) = a_0 + a_1 x + a_2 x^2 + a_3 x^3$, where $x$ is the frequency and $a_i$ are the coefficients.\n",
    "For an open standard, they're $\\text{C}_0$, $\\text{C}_1$, $\\text{C}_2$,\n",
    "$\\text{C}_3$, the first constant term is in femtofarad. For a short\n",
    "standard, they're $\\text{L}_0$, $\\text{L}_1$, $\\text{L}_2$, $\\text{L}_3$,\n",
    "the first constant term is in picohenry.\n",
    "\n",
    "#### Neglected Terms\n",
    "\n",
    "The short standard may sometimes be modeled only in terms of an offset\n",
    "delay and offset loss, without a shunt impedance. Since the behavior of\n",
    "a low-inductance short circuit is reasonably linear at low frequency, one can model it as an extra delay term with acceptable accuracy.\n",
    "\n",
    "A matched load generates little reflection, thus it's often simply modeled as\n",
    "a $Z_0$ termination, its reflection phase shift is assumed to be negligible and\n",
    "is given a zero offset delay.\n",
    "\n",
    "The thru standard is sometimes modeled with an offset delay only, without loss,\n",
    "for two reasons: loss is negligible at low frequencies, and when the Unknown Thru\n",
    "calibration algorithm is used, the exact characteristics of the Thru is unimportant.\n",
    "\n",
    "Conversely, when the Thru is used as a \"flush\" thru, which is the case in most\n",
    "traditional SOLT calibrations - port 1 and port 2 are connected directly without\n",
    "any adapters in the Thru step. Thus, by definition, the thru standard is completely\n",
    "ideal and has zero length, no modeling is required (in the table above, Keysight\n",
    "still gives an offset loss for Load and Thru, but both should be modeled\n",
    "as ideals because the listed offset delay is zero, essentially removing the offset\n",
    "transmission line)."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Preparation\n",
    "\n",
    "<div id=\"preparation\"></div>\n",
    "\n",
    "Equipped with this circuit model, we can start to model the calibration\n",
    "standards.\n",
    "\n",
    "First, we need to import some library definitions, specify the frequency range of our\n",
    "calculation. Here, we used 1 MHz to 9 GHz, with 1001 points. You may want to adjust it\n",
    "for your needs. We also define an `ideal_medium` with a $50 \\space\\Omega$ port\n",
    "impedance for the purpose of some future calculations."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "import skrf\n",
    "from skrf.media import DefinedGammaZ0\n",
    "\n",
    "freq = skrf.Frequency(1, 9000, 1001, \"MHz\")\n",
    "ideal_medium = DefinedGammaZ0(frequency=freq, z0=50)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Ideal Responses\n",
    "\n",
    "It's useful to know the special case first: ideal calibration standards are easily\n",
    "created by calling the `open()`, `short()`, `match()`, and `thru()` methods in the\n",
    "`ideal_medium`, the first three return a 1-port network. The `thru()` method returns\n",
    "a two-port network."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "ideal_open  = ideal_medium.open()\n",
    "ideal_short = ideal_medium.short()\n",
    "ideal_load  = ideal_medium.match()\n",
    "ideal_thru  = ideal_medium.thru()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modeling the Offset Transmission Line\n",
    "\n",
    "To correctly model the offset transmission line, one should use the\n",
    "offset delay, offset loss, and offset $Z_0$ to derive the *propagation\n",
    "constant* ($\\gamma$) and the complex *characteristic impedance* ($Z_c$)\n",
    "of the lossy line. Then, an actual transmission line is defined in those\n",
    "terms.\n",
    "\n",
    "The relationship between the offset line parameters and the propagation\n",
    "constant is given by the following equations by Keysight [[1](#ref1)].\n",
    "They're in fact only approximate, one can obtain more accurate\n",
    "results by calculating the full RLCG transmission line parameters, see\n",
    "[[3](#ref3)] and [[4](#ref4)] for details. However, for practical calibration\n",
    "standards (1-100 ps, 1-25 Gohm/s), the author of this guide found the error\n",
    "is less than 0.001.\n",
    "\n",
    "$$\n",
    "\\begin{gather}\n",
    "\\alpha l = \\frac{\\text{offset loss} \\cdot \\text{offset delay}}{2 \\cdot \\text{ offset }Z_0} \\sqrt{\\frac{f}{10^9}} \\\\\n",
    "\\beta l = 2 \\pi f \\cdot \\text{offset delay} + \\alpha l \\\\\n",
    "\\gamma l = \\alpha l + j\\beta l\\\\\n",
    "Z_c = \\text{offset }Z_0 + (1 - j) \\frac{\\text{offset loss}}{2 \\cdot 2 \\pi f} \\sqrt{\\frac{f}{10^9}}\n",
    "\\end{gather}\n",
    "$$\n",
    "\n",
    "where $\\alpha$ is the attenuation constant of the line, in nepers per meter, $\\beta$ is the phase constant of the line, in radians per meter, $\\gamma = \\alpha + j\\beta$ is the propagation constant of the line, $l$ is the length of the line, $Z_c$ is the complex characteristic impedance of the lossy line.\n",
    "\n",
    "Several facts need to be taken into account. First, the actual length $l$ of the line\n",
    "is irrelevant: what's being calculated here is not just $\\gamma$ but $\\gamma l$, with\n",
    "an implicitly defined length. Thus, if $\\gamma l$ is used as $\\gamma$, the length of\n",
    "the line is always set to unity (i.e. 1 meter). Next, the term $\\sqrt{\\frac{f}{10^9}}$\n",
    "scales the line loss from the nominal 1 GHz value to a given frequency, but this is\n",
    "only valid for coaxial lines, waveguides have a more complicated scaling rule. Finally,\n",
    "the *complex* characteristic impedance $Z_c$ is different from the *real* characteristic\n",
    "impedance offset $Z_0$. Offset $Z_0$ does not include any losses, and it's only used as\n",
    "the port impedance, while $Z_c$ - calculated from offset $Z_0$ and offset loss - is the\n",
    "actual impedance of the lossy line.\n",
    "\n",
    "Let's translate these formulas to code."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def offset_gamma_and_zc(offset_delay, offset_loss, offset_z0=50):\n",
    "    alpha_l = (offset_loss * offset_delay) / (2 * offset_z0)\n",
    "    alpha_l *= np.sqrt(freq.f / 1e9)\n",
    "    beta_l = 2 * np.pi * freq.f * offset_delay + alpha_l\n",
    "    gamma_l = alpha_l + 1j * beta_l\n",
    "    zc = (offset_z0) + (1 - 1j) * (offset_loss / (4 * np.pi * freq.f)) * np.sqrt(freq.f / 1e9)\n",
    "    return gamma_l, zc"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The broadcasting feature in `numpy` is used here. The quantities\n",
    "`alpha_l`, `beta_l`, and `zc` are all frequency-dependent, thus they're\n",
    "arrays, not scalars. But instead of looping over each frequency explicitly\n",
    "and adding them to an array, here, arrays are automatically created by the\n",
    "multiplication of a scalar and a `numpy.array`. We'll continue to use this\n",
    "technique.\n",
    "\n",
    "With the function `offset_gamma_and_zc()` defined, we can now calculate the\n",
    "line constants for the open and short standards by calling it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "gamma_l_open,  zc_open  = offset_gamma_and_zc(29.242e-12, 2.2e9)\n",
    "gamma_l_short, zc_short = offset_gamma_and_zc(31.785e-12, 2.36e9)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "At this point, we already have everything we need to know about this offset line.\n",
    "The other half of the task is straightforward: create a two-port network for\n",
    "this transmission line in scikit-rf, with a propagation constant $\\gamma l$, a\n",
    "characteristic impedance $Z_c$, a port impedance $\\text{offset }Z_0=50\\space\\Omega$,\n",
    "and an unity length (1 meter, because $\\gamma l$ is used as $\\gamma$, and $\\gamma$\n",
    "is measured in meters).\n",
    "\n",
    "It's easy but a bit confusing to perform this task in scikit-rf, it needs elaboration:\n",
    "\n",
    "1. First, create a `DefinedGammaZ0` medium with these arguments: the propagation constant\n",
    "`gamma=gamma_l`, the lossy *medium* impedance `Z0=zc`, and the VNA port impedance\n",
    "`z0=50` (note the spelling difference between `Z0` and `z0`). The created `DefinedGammaZ0`\n",
    "represents a physical medium.\n",
    "\n",
    "2. Then, an actual line with a 1-meter length is derived\n",
    "by calling the medium's `line()` method. But now, pay attention to the arguments\n",
    "`z0=medium.Z0` and `embed=True`. The argument `z0` represents the *line* impedance.\n",
    "Thus, we must set `z0=medium.Z0` (or `zc`, where `medium.Z0` comes from) to get the\n",
    "proper line impedance we want. Finally, we must also set `embed=True` so that the\n",
    "two ports at both ends of the line are set to the port impedance ($50\\space\\Omega$)\n",
    "of the medium.\n",
    "\n",
    "3. The confusing part is that in `DefinedGammaZ0` class, argument `z0` represents the\n",
    "*port* impedance, but in the `line()` method, the same `z0` argument represents the\n",
    "*line* impedance instead! Also, if `z0` is omitted, by default, `line()` uses the\n",
    "*port* impedance of the medium (not *medium* impedance).\n",
    "\n",
    "In a future version, methods for specifying port and line impedances will be simplified,\n",
    "and it hopefully will eliminate this confusing behavior (which is why you'll see a\n",
    "deprecation warning). But for now, the current implementation remains unchanged."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "medium_open = DefinedGammaZ0(\n",
    "    frequency=freq,\n",
    "    gamma=gamma_l_open, z0=zc_open, z0_port=50\n",
    ")\n",
    "line_open = medium_open.line(\n",
    "    d=1, unit='m'\n",
    ")\n",
    "\n",
    "medium_short = DefinedGammaZ0(\n",
    "    frequency=freq,\n",
    "    gamma=gamma_l_short, z0=zc_short, z0_port=50\n",
    ")\n",
    "line_short = medium_short.line(\n",
    "    d=1, unit='m',\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modeling the Shunt Impedance\n",
    "\n",
    "Then, we need to model the shunt impedance of the open and short standards.\n",
    "For the open standard, it's a capacitance. For the short standard, it's\n",
    "an inductance.\n",
    "\n",
    "Both are modeled as a third-degree polynomial, as a function of frequency.\n",
    "In `numpy`, one can quickly define such a function via\n",
    "`np.poly1d([x3, x2, x1, x0])`. This is a higher-order function which accepts\n",
    "a list of coefficients in descending order, and returns a callable polynomial\n",
    "function.\n",
    "\n",
    "After the polynomial is evaluated, we can generate the frequency-dependent\n",
    "capacitors and inductors. The open circuit is modeled as a series\n",
    "`medium.capacitor()` followed by an ideal `medium.short()`. The short circuit\n",
    "is modeled as a series `medium.inductor()` followed by an ideal\n",
    "`medium.short()`.\n",
    "\n",
    "Because the capacitor and inductor are defined with respect to the port impedance,\n",
    "not any particular lossy transmission line, to avoid confusions, we use `ideal_medium`,\n",
    "not `medium_open` or `medium_short` in the following examples (although the latter\n",
    "two are usable, they also use the port impedance)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# use ideal_medium, not medium_open and medium_short to avoid confusions.\n",
    "\n",
    "capacitor_poly = np.poly1d([\n",
    "    -0.1597 * 1e-45,\n",
    "    23.17   * 1e-36,\n",
    "    -310.1  * 1e-27,\n",
    "    49.43   * 1e-15\n",
    "])\n",
    "capacitor_list = capacitor_poly(freq.f)\n",
    "shunt_open = ideal_medium.capacitor(capacitor_list) ** ideal_medium.short()\n",
    "\n",
    "inductor_poly = np.poly1d([\n",
    "    -0.01   * 1e-42,\n",
    "    2.171   * 1e-33,\n",
    "    -108.5  * 1e-24,\n",
    "    2.077   * 1e-12\n",
    "])\n",
    "inductor_list = inductor_poly(freq.f)\n",
    "shunt_short = ideal_medium.inductor(inductor_list) ** ideal_medium.short()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "For the open standard, a series `medium.shunt_capacitor()` terminated by\n",
    "a `medium.open()` could have also been used to get the same result. The\n",
    "`medium.open()` termination is important, because `shunt_capacitor()` creates\n",
    "a two-port network, and the other port needs to be open.  Otherwise, a line\n",
    "terminated solely by a `shunt_capacitor()` produces incorrect S-parameters."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Completion"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, we connect these model components together, and add definitions for the\n",
    "ideal load and Thru, this completes our modeling."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "open_std = line_open ** shunt_open\n",
    "short_std = line_short ** shunt_short\n",
    "load_std = ideal_medium.match()\n",
    "thru_std = ideal_medium.thru()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now you can pass these standards into scikit-rf's calibration routines, or use the `write_touchstone()` method to save them on the disk for future use.\n",
    "\n",
    ".. note::\n",
    "Here, the `open_std`, `short_std` and `load_std` we\n",
    "generated are one-port networks, but most scikit-rf's calibration routines expect a\n",
    "two-port networks as standards since they're used in two-port calibrations. You can\n",
    "use the function `skrf.two_port_reflect()` to generate a two-port network\n",
    "from two one-port networks. For more information, be sure to read the\n",
    "[SOLT calibration](./SOLT.ipynb) example in the doc.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Plotting"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline\n",
    "import matplotlib.pyplot as plt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, let's take a look at the magnitudes and phase shifts of our standards.\n",
    "\n",
    "#### Open"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mag = plt.subplot(1, 1, 1)\n",
    "plt.title(\"Keysight 85033E Open (S11)\")\n",
    "open_std.plot_s_db(color='red', label=\"Magnitude\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 1), loc='upper left', borderaxespad=0)\n",
    "\n",
    "phase = mag.twinx()\n",
    "open_std.plot_s_deg(color='blue', label=\"Phase\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 0.9), loc='upper left', borderaxespad=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mag = plt.subplot(1, 1, 1)\n",
    "plt.title(\"Keysight 85033E Short (S11)\")\n",
    "short_std.plot_s_db(color='red', label=\"Magnitude\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 1), loc='upper left', borderaxespad=0)\n",
    "\n",
    "phase = mag.twinx()\n",
    "short_std.plot_s_deg(color='blue', label=\"Phase\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 0.9), loc='upper left', borderaxespad=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Conclusion\n",
    "\n",
    "As shown in the graphs above, the losses in the standards are extremely low, on the\n",
    "order of 0.01 dB throughout the spectrum. Meanwhile, the phase shift is what really\n",
    "needs compensation for. At 1 GHz, the phase shift has already reached 25 degrees or\n",
    "so."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Code Snippet\n",
    "\n",
    "For convenience, you can reuse the following code snippets to generate calibration standard networks from coefficients in Keysight format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "import skrf\n",
    "from skrf.media import DefinedGammaZ0\n",
    "\n",
    "\n",
    "def keysight_calkit_offset_line(freq, offset_delay, offset_loss, offset_z0, port_z0):\n",
    "    if offset_delay or offset_loss:\n",
    "        alpha_l = (offset_loss * offset_delay) / (2 * offset_z0)\n",
    "        alpha_l *= np.sqrt(freq.f / 1e9)\n",
    "        beta_l = 2 * np.pi * freq.f * offset_delay + alpha_l\n",
    "        zc = offset_z0 + (1 - 1j) * (offset_loss / (4 * np.pi * freq.f)) * np.sqrt(freq.f / 1e9)\n",
    "        gamma_l = alpha_l + beta_l * 1j\n",
    "\n",
    "        medium = DefinedGammaZ0(frequency=freq, z0_port=offset_z0, z0=zc, gamma=gamma_l)\n",
    "        offset_line = medium.line(d=1, unit='m')\n",
    "        return medium, offset_line\n",
    "    else:\n",
    "        medium = DefinedGammaZ0(frequency=freq, z0=offset_z0)\n",
    "        line = medium.line(d=0)\n",
    "        return medium, line\n",
    "\n",
    "\n",
    "def keysight_calkit_open(freq, offset_delay, offset_loss, c0, c1, c2, c3, offset_z0=50, port_z0=50):\n",
    "    medium, line = keysight_calkit_offset_line(freq, offset_delay, offset_loss, offset_z0, port_z0)\n",
    "    # Capacitance is defined with respect to the port impedance offset_z0, not the lossy\n",
    "    # line impedance. In scikit-rf, the return values of `shunt_capacitor()` and `medium.open()`\n",
    "    # methods are (correctly) referenced to the port impedance.\n",
    "    if c0 or c1 or c2 or c3:\n",
    "        poly = np.poly1d([c3, c2, c1, c0])\n",
    "        capacitance = medium.shunt_capacitor(poly(freq.f)) ** medium.open()\n",
    "    else:\n",
    "        capacitance = medium.open()\n",
    "    return line ** capacitance\n",
    "\n",
    "\n",
    "def keysight_calkit_short(freq, offset_delay, offset_loss, l0, l1, l2, l3, offset_z0=50, port_z0=50):\n",
    "    # Inductance is defined with respect to the port impedance offset_z0, not the lossy\n",
    "    # line impedance. In scikit-rf, the return values of `inductor()` and `medium.short()`\n",
    "    # methods are (correctly) referenced to the port impedance.\n",
    "    medium, line = keysight_calkit_offset_line(freq, offset_delay, offset_loss, offset_z0, port_z0)\n",
    "    if l0 or l1 or l2 or l3:\n",
    "        poly = np.poly1d([l3, l2, l1, l0])\n",
    "        inductance = medium.inductor(poly(freq.f)) ** medium.short()\n",
    "    else:\n",
    "        inductance = medium.short()\n",
    "    return line ** inductance\n",
    "\n",
    "\n",
    "def keysight_calkit_load(freq, offset_delay=0, offset_loss=0, offset_z0=50, port_z0=50):\n",
    "    medium, line = keysight_calkit_offset_line(freq, offset_delay, offset_loss, offset_z0, port_z0)\n",
    "    load = medium.match()\n",
    "    return line ** load\n",
    "\n",
    "\n",
    "def keysight_calkit_thru(freq, offset_delay=0, offset_loss=0, offset_z0=50, port_z0=50):\n",
    "    medium, line = keysight_calkit_offset_line(freq, offset_delay, offset_loss, offset_z0, port_z0)\n",
    "    thru = medium.thru()\n",
    "    return line ** thru\n",
    "\n",
    "\n",
    "freq = skrf.Frequency(1, 9000, 1001, \"MHz\")\n",
    "open_std = keysight_calkit_open(\n",
    "    freq,\n",
    "    offset_delay=29.242e-12, offset_loss=2.2e9,\n",
    "    c0=49.43e-15, c1=-310.1e-27, c2=23.17e-36, c3=-0.1597e-45\n",
    ")\n",
    "short_std = keysight_calkit_short(\n",
    "    freq,\n",
    "    offset_delay=31.785e-12, offset_loss=2.36e9,\n",
    "    l0=2.077e-12, l1=-108.5e-24, l2=2.171e-33, l3=-0.01e-42\n",
    ")\n",
    "load_std = keysight_calkit_load(freq)\n",
    "thru_std = keysight_calkit_thru(freq)\n",
    "\n",
    "# hypothetically, the S-parameters of the same 50-ohm short standard as measured by a 75-ohm VNA\n",
    "short_std_50_on_75 = keysight_calkit_short(\n",
    "    freq,\n",
    "    offset_delay=31.785e-12, offset_loss=2.36e9,\n",
    "    l0=2.077e-12, l1=-108.5e-24, l2=2.171e-33, l3=-0.01e-42,\n",
    "    offset_z0=50, port_z0=75\n",
    ")\n",
    "# hypothetically, a 49.992-ohm short standard for use with a 50-ohm VNA\n",
    "short_std_49_on_50 = keysight_calkit_short(\n",
    "    freq,\n",
    "    offset_delay=31.785e-12, offset_loss=2.36e9,\n",
    "    l0=2.077e-12, l1=-108.5e-24, l2=2.171e-33, l3=-0.01e-42,\n",
    "    offset_z0=49.992, port_z0=50\n",
    ")\n",
    "# hypothetically, a 75-ohm short standard for a 75-ohm VNA\n",
    "short_std_75 = keysight_calkit_short(\n",
    "    freq,\n",
    "    offset_delay=31.785e-12, offset_loss=2.36e9,\n",
    "    l0=2.077e-12, l1=-108.5e-24, l2=2.171e-33, l3=-0.01e-42,\n",
    "    offset_z0=75, port_z0=75\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Example: Rohde & Schwarz / Anritsu Coefficient Format\n",
    "\n",
    "On Rohde & Schwarz and Anritsu VNAs, a slightly different format is used to define the coefficients. Here's an example of a Maury Microwave 8050CK10, a 3.5 mm, DC to 26.5 GHz calibration kit defined in Rohde & Schwartz's format [[5](#ref5)].\n",
    "\n",
    "|   Parameter  |            Unit             |    Open    |   Short   |  Load   |   Thru   |\n",
    "| ------------ | --------------------------- | ---------- | --------- | ------- | -------- |\n",
    "| $\\text{C}_0$ |  $10^{−15} \\text{ F}$       |  62.54     |           |         |          |\n",
    "| $\\text{C}_1$ |  $10^{−15} \\text{ F/GHz}$   |  1284.0    |           |         |          |\n",
    "| $\\text{C}_2$ |  $10^{−15} \\text{ F/GHz}^2$ |   107.6    |           |         |          |\n",
    "| $\\text{C}_3$ |  $10^{−15} \\text{ F/GHz}^3$ |  -1.886    |           |         |          |\n",
    "| $\\text{L}_0$ |  $10^{−12} \\text{ H}$       |            |    0      |         |          |\n",
    "| $\\text{L}_1$ |  $10^{−12} \\text{ H/GHz}$   |            |    0      |         |          |\n",
    "| $\\text{L}_2$ |  $10^{−12} \\text{ H/GHz}^2$ |            |    0      |         |          |\n",
    "| $\\text{L}_3$ |  $10^{−12} \\text{ H/GHz}^3$ |            |    0      |         |          |\n",
    "|  Resistance  |         $\\Omega$            |            |           |    50   |          |\n",
    "| Offset Length|            mm               |  4.344     |  5.0017   |     0   |  17.375  |\n",
    "| Offset Loss  |$\\text{dB / }\\sqrt{\\text{GHz}}$| 0.0033   |  0.0038   |     0   |   0.0065 |\n",
    "\n",
    "### Modeling the Offset Transmission Line\n",
    "\n",
    "As shown, it's essentially the same circuit model, the only difference is that the offset transmission line is defined in different units of measurements: offset delay is defined as a physical length instead of a time delay, offset loss is defined in decibel, the offset $Z_0$ is defined to be $50 \\space\\Omega$ and unlisted.\n",
    "\n",
    "We can reuse the same calculations in the Keysight model after a simple unit conversion using these equations [[2](#ref2)].\n",
    "\n",
    "$$\n",
    "\\begin{gather}\n",
    "\\text{D'} = \\frac{D \\cdot \\sqrt{\\epsilon_r}}{c_0} \\\\\n",
    "\\text{L'} = \\frac{L \\cdot Z_0}{D' \\cdot 20 \\log_{10}{(e)}}\n",
    "\\end{gather}\n",
    "$$\n",
    "\n",
    "where $D$ and $L$ are the offset length (meter) and offset loss ($\\text{dB / }\\sqrt{\\text{GHz}}$) in the R&S model, $D'$ and $L'$ are the offset delay (second) and offset loss ($\\Omega$ / s) in Keysight's model, $\\epsilon_r$ is the dielectric constant, it's air by definition, thus $\\epsilon_r = 1$, and $c_0$ is the speed of light. The term $20 \\log_{10}{(e)}$ is a conversion from decibel to neper."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def rs_to_keysight(rs_offset_length, rs_offset_loss, offset_z0=50):\n",
    "    offset_delay = rs_offset_length / skrf.constants.c\n",
    "    offset_loss = skrf.mathFunctions.db_2_np(rs_offset_loss * offset_z0 / offset_delay)\n",
    "    return offset_delay, offset_loss"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After unit conversion, we can define standards just like how calibration standards in Keysight-style\n",
    "coefficients are defined."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "offset_delay, offset_loss = rs_to_keysight(4.344e-3, 0.0033)\n",
    "gamma_l, zc = offset_gamma_and_zc(offset_delay, offset_loss)\n",
    "medium_open = DefinedGammaZ0(\n",
    "    frequency=freq,\n",
    "    gamma=gamma_l, z0=zc, z0_port=50\n",
    ")\n",
    "line_open = medium_open.line(\n",
    "    d=1, unit='m'\n",
    ")\n",
    "\n",
    "offset_delay, offset_loss = rs_to_keysight(5.0017e-3, 0.0038)\n",
    "gamma_l, zc = offset_gamma_and_zc(offset_delay, offset_loss)\n",
    "medium_short = DefinedGammaZ0(\n",
    "    frequency=freq,\n",
    "    gamma=gamma_l, z0=zc, z0_port=50\n",
    ")\n",
    "line_short = medium_short.line(\n",
    "    d=1, unit='m'\n",
    ")\n",
    "\n",
    "offset_delay, offset_loss = rs_to_keysight(17.375e-3, 0.0065)\n",
    "gamma_l, zc = offset_gamma_and_zc(offset_delay, offset_loss)\n",
    "medium_thru = DefinedGammaZ0(\n",
    "    frequency=freq,\n",
    "    gamma=gamma_l, z0=zc, z0_port=50\n",
    ")\n",
    "line_thru = medium_thru.line(\n",
    "    d=1, unit='m'\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Modeling the Shunt Impedance\n",
    "\n",
    "The definition of shunt impedance is identical to the Keysight format.\n",
    "\n",
    "But, beware of the units used for the capacitance and inductance! In the\n",
    "given table, the capacitances are given in $10^{−15} \\text{ F}$, $10^{−15} \\text{ F/GHz}$,\n",
    "$10^{−15} \\text{ F/GHz}^2$, and\n",
    "$10^{−15} \\text{ F/GHz}^3$. For Keysight and Anritsu VNAs, they're given in $10^{-15} \\text{ F}$,\n",
    "$10^{-27} \\text{ F/Hz}$, $10^{-36} \\text{ F/Hz}^2$ and $10^{-45} \\text{ F/Hz}^3$. Inductance\n",
    "units have the same differences. Always double-check the units before start modeling. To\n",
    "convert the units from the first to the second format, multiply $x_1$, $x_2$ and $x_3$\n",
    "by 1000 (don't change the constant term $x_0$). For consistency, we'll use the second format\n",
    "in the code.\n",
    "\n",
    "Since the inductance in the short standard is neglected, only the capacitance in the open\n",
    "standard is modeled, the short is modeled as ideal."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "capacitor_poly = np.poly1d([\n",
    "    -0.001886 * 1000e-45,\n",
    "     0.1076   * 1000e-36,\n",
    "    -1.284    * 1000e-27,\n",
    "    62.54     * 1e-15\n",
    "])\n",
    "capacitor_open = capacitor_poly(freq.f)\n",
    "shunt_open = ideal_medium.shunt_capacitor(capacitor_open) ** ideal_medium.open()\n",
    "# or: shunt_open = ideal_medium.capacitor(capacitor_open) ** ideal_medium.short()\n",
    "# see the Keysight example for explanation.\n",
    "\n",
    "shunt_short = ideal_medium.short()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Completion\n",
    "\n",
    "Finally, we connect these model components together."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "open_std = line_open ** shunt_open\n",
    "short_std = line_short ** shunt_short\n",
    "load_std = ideal_medium.match()\n",
    "thru_std = line_thru"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Plotting\n",
    "\n",
    "Again, let's examine the behaviors of the finished standards.\n",
    "\n",
    "#### Open"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mag = plt.subplot(1, 1, 1)\n",
    "plt.title(\"Maury Microwave 8050CK10 Open (S11)\")\n",
    "open_std.plot_s_db(color='red', label=\"Magnitude\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 1), loc='upper left', borderaxespad=0)\n",
    "\n",
    "phase = mag.twinx()\n",
    "open_std.plot_s_deg(color='blue', label=\"Phase\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 0.9), loc='upper left', borderaxespad=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Short"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mag = plt.subplot(1, 1, 1)\n",
    "plt.title(\"Maury Microwave 8050CK10 Short (S11)\")\n",
    "short_std.plot_s_db(color='red', label=\"Magnitude\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 1), loc='upper left', borderaxespad=0)\n",
    "\n",
    "phase = mag.twinx()\n",
    "short_std.plot_s_deg(color='blue', label=\"Phase\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 0.9), loc='upper left', borderaxespad=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### Thru"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mag = plt.subplot(1, 1, 1)\n",
    "plt.title(\"Maury Microwave 8050CK10 Thru (S21)\")\n",
    "thru_std.s21.plot_s_db(color='red', label=\"Magnitude\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 1), loc='upper left', borderaxespad=0)\n",
    "\n",
    "phase = mag.twinx()\n",
    "thru_std.s21.plot_s_deg(color='blue', label=\"Phase\")\n",
    "plt.legend(bbox_to_anchor=(0.73, 0.9), loc='upper left', borderaxespad=0)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Conclusion\n",
    "\n",
    "The results are similar to the Keysight calibration standards. The S21 graph for the\n",
    "Thru standard explains why adding an electrical delay sometimes can serve as a crude\n",
    "but usable calibration method (\"port extension\") for VNA measurements. Again, losses\n",
    "are extremely low, phase shift is the source of non-ideal properties in the standards. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Code Snippet\n",
    "\n",
    "For convenience, you can reuse the following code snippet to generate calibration standard networks from coefficients in Rhode & Swartz and Anritsu format."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "\n",
    "import skrf\n",
    "from skrf.media import DefinedGammaZ0\n",
    "\n",
    "\n",
    "def rs_to_keysight(rs_offset_length, rs_offset_loss, offset_z0=50):\n",
    "    offset_delay = rs_offset_length / skrf.constants.c\n",
    "    offset_loss = skrf.mathFunctions.db_2_np(rs_offset_loss * offset_z0 / offset_delay)\n",
    "    return offset_delay, offset_loss\n",
    "\n",
    "\n",
    "def rs_calkit_offset_line(freq, rs_offset_length, rs_offset_loss, offset_z0, port_z0):\n",
    "    if rs_offset_length or rs_offset_loss:\n",
    "        offset_delay, offset_loss = rs_to_keysight(rs_offset_length, rs_offset_loss)\n",
    "\n",
    "        alpha_l = (offset_loss * offset_delay) / (2 * offset_z0)\n",
    "        alpha_l *= np.sqrt(freq.f / 1e9)\n",
    "        beta_l = 2 * np.pi * freq.f * offset_delay + alpha_l\n",
    "        zc = offset_z0 + (1 - 1j) * (offset_loss / (4 * np.pi * freq.f)) * np.sqrt(freq.f / 1e9)\n",
    "        gamma_l = alpha_l + beta_l * 1j\n",
    "\n",
    "        medium = DefinedGammaZ0(frequency=freq, z0_port=offset_z0, z0=zc, gamma=gamma_l)\n",
    "        offset_line = medium.line(d=1, unit='m')\n",
    "        return medium, offset_line\n",
    "    else:\n",
    "        medium = DefinedGammaZ0(frequency=freq, z0=offset_z0)\n",
    "        line = medium.line(d=0)\n",
    "        return medium, line\n",
    "\n",
    "\n",
    "def rs_calkit_open(freq, offset_length, offset_loss, c0, c1, c2, c3, offset_z0=50, port_z0=50):\n",
    "    # Capacitance is defined with respect to the port impedance offset_z0, not the lossy\n",
    "    # line impedance. In scikit-rf, the return values of `shunt_capacitor()` and `medium.open()`\n",
    "    # methods are (correctly) referenced to the port impedance.\n",
    "    medium, line = rs_calkit_offset_line(freq, offset_length, offset_loss, offset_z0, port_z0)\n",
    "    if c0 or c1 or c2 or c3:\n",
    "        poly = np.poly1d([c3, c2, c1, c0])\n",
    "        capacitance = medium.shunt_capacitor(poly(freq.f)) ** medium.open()\n",
    "    else:\n",
    "        capacitance = medium.open()\n",
    "    return line ** capacitance\n",
    "\n",
    "\n",
    "def rs_calkit_short(freq, offset_length, offset_loss, l0, l1, l2, l3, offset_z0=50, port_z0=50):\n",
    "    # Inductance is defined with respect to the port impedance offset_z0, not the lossy\n",
    "    # line impedance. In scikit-rf, the return values of `inductor()` and `medium.short()`\n",
    "    # methods are (correctly) referenced to the port impedance.\n",
    "    medium, line = rs_calkit_offset_line(freq, offset_length, offset_loss, offset_z0, port_z0)\n",
    "    if l0 or l1 or l2 or l3:\n",
    "        poly = np.poly1d([l3, l2, l1, l0])\n",
    "        inductance = medium.inductor(poly(freq.f)) ** medium.short()\n",
    "    else:\n",
    "        inductance = medium.short()\n",
    "    return line ** inductance\n",
    "\n",
    "\n",
    "def rs_calkit_load(freq, offset_length=0, offset_loss=0, offset_z0=50, port_z0=50):\n",
    "    medium, line = rs_calkit_offset_line(freq, offset_length, offset_loss, offset_z0, port_z0)\n",
    "    load = medium.match()\n",
    "    return line ** load\n",
    "\n",
    "\n",
    "def rs_calkit_thru(freq, offset_length=0, offset_loss=0, offset_z0=50, port_z0=50):\n",
    "    medium, line = rs_calkit_offset_line(freq, offset_length, offset_loss, offset_z0, port_z0)\n",
    "    thru = medium.thru()\n",
    "    return line ** thru\n",
    "\n",
    "\n",
    "freq = skrf.Frequency(1, 9000, 1001, \"MHz\")\n",
    "open_std = rs_calkit_open(\n",
    "    freq,\n",
    "    offset_length=4.344e-3, offset_loss=0.0033,\n",
    "    # Due to unit differences, the numerical values of c1, c2 and c3\n",
    "    # must be multiplied by 1000 from the R&S datasheet value. For\n",
    "    # Anritsu, this is not needed. Check the units on your datasheet!\n",
    "    c0=62.54     * 1e-15,\n",
    "    c1=-1.284    * 1000e-27,\n",
    "    c2=0.1076    * 1000e-36,\n",
    "    c3=-0.001886 * 1000e-45\n",
    ")\n",
    "short_std = rs_calkit_short(\n",
    "    freq,\n",
    "    offset_length=5.0017e-3, offset_loss=0.0038,\n",
    "    l0=0, l1=0, l2=0, l3=0\n",
    ")\n",
    "load_std = rs_calkit_load(freq)\n",
    "thru_std = rs_calkit_thru(\n",
    "    freq,\n",
    "    offset_length=17.375e-3, offset_loss=0.0065\n",
    ")\n",
    "\n",
    "# hypothetically, the S-parameters of the same 50-ohm short standard as measured by a 75-ohm VNA\n",
    "short_std_50_on_75 = rs_calkit_short(\n",
    "    freq,\n",
    "    offset_length=5.0017e-3, offset_loss=0.0038,\n",
    "    l0=0, l1=0, l2=0, l3=0,\n",
    "    offset_z0=50, port_z0=75\n",
    ")\n",
    "# hypothetically, a 49.992-ohm short standard for use with a 50-ohm VNA\n",
    "short_std_49_on_50 = rs_calkit_short(\n",
    "    freq,\n",
    "    offset_length=5.0017e-3, offset_loss=0.0038,\n",
    "    l0=0, l1=0, l2=0, l3=0,\n",
    "    offset_z0=49.992, port_z0=50\n",
    ")\n",
    "# hypothetically, a 75-ohm short standard for a 75-ohm VNA\n",
    "short_std_75 = rs_calkit_short(\n",
    "    freq,\n",
    "    offset_length=5.0017e-3, offset_loss=0.0038,\n",
    "    l0=0, l1=0, l2=0, l3=0,\n",
    "    offset_z0=75, port_z0=75\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": []
   },
   "source": [
    "## References\n",
    "\n",
    "<div id=\"ref1\"></div>\\[1]: [Specifying Calibration Standards and Kits for Agilent Vector Network Analyzers](https://www.keysight.com/zz/en/assets/7018-01375/application-notes/5989-4840.pdf). See Equation 36, 37 for the propagation constant formulas of the offset transmission line.\n",
    "\n",
    "<div id=\"ref2\"></div>\\[2]: [METAS VNA Tools II - Math Reference V2.1](https://www.metas.ch/dam/metas/de/data/Fachbereiche/Hochfrequenz/vna-tools/vnatoolsmath-v2-1-0.pdf). See Page 26, 27 for formulas of the Keysight and R&S coefficients. The Keysight formulas are equivalent to [[1](#ref1)].\n",
    "\n",
    "<div id=\"ref3\"></div>\\[3]: S-Parameters for Signal Integrity, Peter J. Pupalaikis. Page 481.\n",
    "\n",
    "<div id=\"ref4\"></div>\\[4]: [Effect of Loss on VNA Calibration Standards](https://loco.lab.asu.edu/loco-memos/edges_reports/report_20130807.pdf). Source of the Keysight 85033E example.\n",
    "\n",
    "<div id=\"ref5\"></div>\\[5]: [Maury Microwave 3.5mm Coaxial Calibration Kit User Guide](https://www.maurymw.com/CalKit_Explorer/downloads/8050-511.pdf). Source of the Maury Microwave 8050CK10 example. The coefficients of this calibration kit are given in multiple formats (Anritsu, Keysight, and R&S). You can compare and contrast their differences."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
